package main

import (
	"fmt"
	"math/rand"
	"testing"
	"time"
)

func main()  {
	//利用函数消费发出的事件
	done := make(chan interface{})
	time.AfterFunc(10*time.Second, func() { close(done) }) //声明标准done channel，10秒后关闭，给goreoutine工作时间

	const timeout = 2 * time.Second               //设置超时时间，将心跳间隔和超时时间联系起来
	heartbeat, results := doWork(done, timeout/2) //timeout/2,心跳有额外的响应时间，超时不太敏感
	for {
		select {
		case _, ok := <-heartbeat: //处理心跳
			if ok == false {
				return
			}
			fmt.Println("pulse")
		case r, ok := <-results: //处理results channel
			if ok == false {
				return
			}
			fmt.Printf("results %v\n", r.Second())
		case <-time.After(timeout): //没有收到心跳或消息，会超时
			return
		}
	}

	//两次迭代后停止goroutine,不关闭channel，模拟一个产生了异常的goroutine
	doWork := func(
		done <-chan interface{},
		pulseInterval time.Duration,
	) (<-chan interface{},<-chan time.Time) {
		heartbeat := make(chan interface{})
		results := make(chan time.Time)
		go func() {
			pulse := time.Tick(pulseInterval)
			workGen := time.Tick(2*pulseInterval)
			sendPulse := func() {
				select {
				case heartbeat <-struct{}{}:
				default:
				}
			}
			sendResult := func(r time.Time) {
				for {
					select {
					case <-pulse:
						sendPulse()
					case results <- r:
						return
					}
				}
			}
			for i := 0;i<2;i++ {
				select {
				case <-done:
					return
				case <-pulse:
					sendPulse()
				case r := <-workGen:
					sendResult(r)
				}
			}
		}()
		return heartbeat,results
	}

	done := make(chan interface{})
	time.AfterFunc(10*time.Second,func() { close(done) })

	const timeout = 2 * time.Second
	heartbeat,results := doWork(done,timeout/2)
	for {
		select {
		case _,ok := <-heartbeat:
			if ok == false {
				return
			}
			fmt.Println("pulse")
		case r,ok := <-results:
			if ok == false {
				return
			}
			fmt.Printf("results %v\n",r)
		case <-time.After(timeout):
			fmt.Println("worker goroutine is not healthy")
			return
		}
	}


//测试单元有效的，每个工作单元在开始之前发送心跳
	doWork := func(done <-chan interface{},) (<-chan interface{}, <-chan int) {
		heartbeatStream := make(chan interface{},1)    //创建一个缓冲大小为1的channel，确保即使没有接发送的消息，至少也会发出一个心跳
		workStream := make(chan int)
		go func(){
			defer close(heartbeatStream)
			defer close(workStream)

			for i:=0;i<10;i++ {
				select {   //将发送results和心跳分开，若接收者没有准备好接收结果，它仍将接收到一个心跳
				case heartbeatStream <- struct{}{}:
				default:   //防止没人接收心跳，增加的默认逻辑，监听但是没有收到第一个心跳，接收者仍然可以收到心跳
				}

				select {
				case <-done:
					return
				case workStream <- rand.Intn(10):
				}
			}
		}()

		return heartbeatStream,workStream
	}

	done := make(chan interface{})
	defer close(done)

	heartbeat,results := doWork(done)
	for {
		select {
		case _,ok := <-heartbeat:
			if ok {
				fmt.Println("pulse")
			} else {
				return
			}
		case r,ok := <-results:
			if ok {
				fmt.Printf("results %v\n",r)
			} else {
				return
			}
		}
	}

//doWork生成器的测试函数
func TestDoWork_GeneratestAllNumbers(t *testing.T) {
	done := make(chan interface{})
	defer close(done)

	intSlice := []int{0,1,2,3,5}
	heartbeat,results := Dowork(done,intSlice...)

	<-heartbeat    //等待goroutine开始处理迭代的信号

	i := 0
	for r := range results {
		if expected := intSlice[i]; r != expected {
			t.Errorf("intex %v: expected %v,but received %v,",i,expected,r)
		}
		i++
	}
}

	//使用心跳进行测试：
	func DoWork(
		done <-chan interface{},
	pulseInterval time.Duration,
	nums ...int,
	) (<-chan interface{},<-chan int) {
	heartbeat := make(chan interface{},1)
	intStream := make(chan int)
	go func() {
	defer close(heartbeat)
	defer close(intStream)

	time.Sleep(2*time.Second)

	pulse := time.Tick(pulseInterval)
	numloop:   //使用标签来简化内部循环
	for _,n := range nums {
	for {  //需要两个循环，一个循环遍历数列，内部循环持续执行，直到intStream中的数字成功发送
	select {
	case <-done:
	return
	case <-pulse:
	select {
	case heartbeat <- struct{}{}:
	default:
	}
	case intStream <- n:
	continue numloop   //跳回numloop标签继续执行外部循环
	}
	}
	}
	}()

	return heartbeat,intStream
	}

	func TestDoWork_GeneratestAllNumbers(t *testing.T) {
	done := make(chan interface{})
	defer close(done)

	intSlice := []int{0,1,2,3,5}
	const timeout = 2*time.Second
	heartbeat,results := DoWork(done,timeout/2,intSlice...)

	<- heartbeat   //等待第一次心跳到达，来确认goroutine已经进入循环

	i := 0
	for {
	select {
	case r,ok := <-results:
	if ok == false {
	return
	} else if expected := intSlice[i];r != expected {
	t.Errorf (
	"index %v: expected %v,but received %v,",
	i,
	expected,
	r,
	)
	}
	i++
	case <-heartbeat:  //接收心跳，防止超时
	case <-time.After(timeout):
	t.Fatal("test timed out")
	}
	}
	}


}